Skip to content

Conversation

@matt-aitken
Copy link
Member

What changed

  • Upgraded recharts to 2.15.2
  • Added multiple chart types and components: big number, line, stacked, bar (including zoomable & reference line), big dataset bar, and usage graph
  • Implemented custom legend with animated values, tooltip showing x-axis data, and hover/highlight behaviors for stacks and legend
  • Added loading, no-data, and invalid chart states plus loading spinners and improved loading animations/layout
  • Storybook integration: initial charts setup, separate chart files, alphabetized menu, chart state toggles, and story updates
  • Interaction & UX improvements: zooming (drag/select), crosshair pointer, show/select dates while zooming, prevent text selection on drag, hide mouse wheel zoom, capped legend items, axis/legend styling tweaks, better spacing, and min-height for charts
  • Data & state handling: moved date data to route for unified zooming, moved chartState to main Chart component, moved hard-coded/mock data out of components, and set chart data when zooming to start/end dates
  • Performance & animation: turned off/reduced chart animations, sped up animated numbers, removed hover transitions for bars
  • New UI primitives and layout: Card component, small card updates, SVG icons, improved segmented control and popover variants, table improvements (resizable columns, filtering, sorting, scrolling fixes)
  • Various fixes and polish: tooltip style fixes, legend value updates, hover/leave state resets, bar width fixes for small datasets, type/import fixes, and numerous small style/typo tweaks

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/webapp/app/components/runs/v3/SharedFilters.tsx (1)

19-33: Remove unused imports.

useRef (line 19), SpanPresenter (line 32), and useLocation (line 33) are imported but not used in this file. These should be removed to avoid confusion.

🧹 Suggested fix
-import { startTransition, useCallback, useEffect, useRef, useState, type ReactNode } from "react";
+import { startTransition, useCallback, useEffect, useState, type ReactNode } from "react";
 import { ShortcutDefinition } from "~/hooks/useShortcutKeys";
-import { SpanPresenter } from "~/presenters/v3/SpanPresenter.server";
-import { useLocation } from "@remix-run/react";
🤖 Fix all issues with AI agents
In
`@apps/webapp/app/routes/_app.orgs`.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx:
- Around line 507-520: handleTimeFilterChange currently updates URL search
params via replaceSearchParams (AITimeFilter) but QueryEditorForm never reads
those params or auto-submits, so AI updates don't apply; fix by wiring
QueryEditorForm to react to search param changes: either have QueryEditorForm
read the period/from/to params from the URL (useSearchParams) and trigger its
submit handler when they change, or change the parent to pass the AITimeFilter
values as props to QueryEditorForm and call its onSubmit programmatically when
handleTimeFilterChange runs; update references: handleTimeFilterChange,
replaceSearchParams, QueryEditorForm, and any submit handler (e.g., onSubmit or
submitQuery) to ensure form re-submission occurs after the params update.
- Around line 366-368: The time filter state variables period, from and to are
never initialized from or synchronized with URL search params, so changes made
by handleTimeFilterChange update the URL but not the component state (and
refresh loses the values); fix by reading search params (e.g., via
useSearchParams or equivalent) on mount and whenever params change and
initializing/setting period, setPeriod, from, setFrom, to, setTo accordingly,
and ensure handleTimeFilterChange also updates these state setters (or
alternatively lift the time filter state to the parent and pass it down so the
URL and form stay in sync).
♻️ Duplicate comments (2)
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx (1)

370-372: Regex for triggered_at detection has known limitations.

The regex can match triggered_at appearing in string literals or comments, which could incorrectly disable the time filter UI. This was noted in a previous review.

apps/webapp/app/components/code/TSQLResultsTable.tsx (1)

63-66: Remove the stray doc comment above getFormattedValue.
It references the fuzzy filter but sits before the formatter docs, which is confusing.

Suggested change
-/**
- * Fuzzy filter function using match-sorter ranking
- */
🧹 Nitpick comments (7)
apps/webapp/app/components/runs/v3/SharedFilters.tsx (4)

274-279: Use type instead of interface.

Per coding guidelines, prefer type aliases over interfaces in TypeScript.

♻️ Suggested fix
-/** Values passed to onApply callback when a time filter is applied */
-export interface TimeFilterApplyValues {
-  period?: string;
-  from?: string;
-  to?: string;
-}
+/** Values passed to onApply callback when a time filter is applied */
+export type TimeFilterApplyValues = {
+  period?: string;
+  from?: string;
+  to?: string;
+};

281-291: Use type instead of interface.

Same as above—per coding guidelines, prefer type aliases.

♻️ Suggested fix
-export interface TimeFilterProps {
+export type TimeFilterProps = {
   defaultPeriod?: string;
   period?: string;
   from?: string;
   to?: string;
   /** Label name used in the filter display, defaults to "Created" */
   labelName?: string;
   applyShortcut?: ShortcutDefinition | undefined;
   /** Callback when the user applies a time filter selection, receives the applied values */
   onValueChange?: (values: TimeFilterApplyValues) => void;
-}
+};

416-496: Consider clarifying callback semantics.

The relationship between onApply and onValueChange may be confusing to consumers:

  • onValueChange is checked first to determine controlled vs URL mode
  • onApply fires unconditionally after either path

If both callbacks are provided, both will be invoked. Consider documenting this behavior or consolidating to a single callback to reduce ambiguity.


827-831: Simplify with nullish coalescing.

Minor readability improvement using ?? instead of a ternary.

♻️ Suggested fix
-              shortcut={applyShortcut ? applyShortcut : {
+              shortcut={applyShortcut ?? {
                 modifiers: ["mod"],
                 key: "Enter",
                 enabledOnInputElements: true,
               }}
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx (1)

757-774: Consider adding error handling for clipboard operations.

The navigator.clipboard.writeText calls can fail if clipboard permissions are denied. While not critical, adding a try-catch with user feedback would improve UX.

♻️ Suggested improvement
  const handleCopyCSV = () => {
    const csv = rowsToCSV(rows, columns);
-   navigator.clipboard.writeText(csv);
-   setIsOpen(false);
+   navigator.clipboard.writeText(csv)
+     .then(() => setIsOpen(false))
+     .catch((err) => console.error("Failed to copy:", err));
  };
apps/webapp/app/components/code/TSQLResultsTable.tsx (2)

217-221: Prefer a type alias for ColumnMeta.
This aligns with the project’s TS style guidance.

Suggested change
-interface ColumnMeta {
-  outputColumn: OutputColumnMetadata;
-  alignment: "left" | "right";
-}
+type ColumnMeta = {
+  outputColumn: OutputColumnMetadata;
+  alignment: "left" | "right";
+};
As per coding guidelines, prefer `type` over `interface` in TS.

699-758: Use a button for the copy affordance (keyboard access).
The clickable <span> isn’t focusable; a <button> improves accessibility without changing layout.

Suggested change
-        <span
+        <button
+          type="button"
+          aria-label="Copy cell value"
           onClick={(e) => {
             e.stopPropagation();
             e.preventDefault();
             copy();
           }}
-          className="absolute right-1 top-1/2 z-10 flex -translate-y-1/2 cursor-pointer"
+          className="absolute right-1 top-1/2 z-10 flex -translate-y-1/2 cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500/50"
         >
           <SimpleTooltip
             button={
               <span
                 className={cn(
                   "flex size-6 items-center justify-center rounded border border-charcoal-650 bg-charcoal-750",
                   copied
                     ? "text-green-500"
                     : "text-text-dimmed hover:border-charcoal-600 hover:bg-charcoal-700 hover:text-text-bright"
                 )}
               >
                 {copied ? (
                   <ClipboardCheckIcon className="size-3.5" />
                 ) : (
                   <ClipboardIcon className="size-3.5" />
                 )}
               </span>
             }
             content={copied ? "Copied!" : "Copy"}
             disableHoverableContent
           />
-        </span>
+        </button>
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c579d04 and 9c1ee99.

📒 Files selected for processing (3)
  • apps/webapp/app/components/code/TSQLResultsTable.tsx
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Use types over interfaces for TypeScript
Avoid using enums; prefer string unions or const objects instead

**/*.{ts,tsx}: Always import tasks from @trigger.dev/sdk, never use @trigger.dev/sdk/v3 or deprecated client.defineJob pattern
Every Trigger.dev task must be exported and have a unique id property with no timeouts in the run function

Files:

  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
  • apps/webapp/app/components/code/TSQLResultsTable.tsx
{packages/core,apps/webapp}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use zod for validation in packages/core and apps/webapp

Files:

  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
  • apps/webapp/app/components/code/TSQLResultsTable.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use function declarations instead of default exports

Import from @trigger.dev/core using subpaths only, never import from root

Files:

  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
  • apps/webapp/app/components/code/TSQLResultsTable.tsx
apps/webapp/app/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

Access all environment variables through the env export of env.server.ts instead of directly accessing process.env in the Trigger.dev webapp

Files:

  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
  • apps/webapp/app/components/code/TSQLResultsTable.tsx
apps/webapp/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

apps/webapp/**/*.{ts,tsx}: When importing from @trigger.dev/core in the webapp, use subpath exports from the package.json instead of importing from the root path
Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webapp

Access environment variables via env export from apps/webapp/app/env.server.ts, never use process.env directly

Files:

  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
  • apps/webapp/app/components/code/TSQLResultsTable.tsx
**/*.{js,ts,jsx,tsx,json,md,yaml,yml}

📄 CodeRabbit inference engine (AGENTS.md)

Format code using Prettier before committing

Files:

  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
  • apps/webapp/app/components/code/TSQLResultsTable.tsx
🧠 Learnings (2)
📚 Learning: 2025-12-08T15:19:56.823Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2760
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx:278-281
Timestamp: 2025-12-08T15:19:56.823Z
Learning: In apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx, the tableState search parameter uses intentional double-encoding: the parameter value contains a URL-encoded URLSearchParams string, so decodeURIComponent(value("tableState") ?? "") is required to fully decode it before parsing with new URLSearchParams(). This pattern allows bundling multiple filter/pagination params as a single search parameter.

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
📚 Learning: 2025-11-27T16:26:37.432Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T16:26:37.432Z
Learning: Applies to internal-packages/database/**/*.{ts,tsx} : Use Prisma for database interactions in internal-packages/database with PostgreSQL

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
🧬 Code graph analysis (2)
apps/webapp/app/components/runs/v3/SharedFilters.tsx (2)
apps/webapp/app/hooks/useShortcutKeys.tsx (1)
  • ShortcutDefinition (14-19)
apps/webapp/app/components/runs/v3/RunFilters.tsx (1)
  • filterIcon (227-263)
apps/webapp/app/components/code/TSQLResultsTable.tsx (7)
internal-packages/tsql/src/query/schema.ts (2)
  • column (318-330)
  • OutputColumnMetadata (242-257)
internal-packages/clickhouse/src/index.ts (1)
  • OutputColumnMetadata (61-61)
packages/core/src/v3/index.ts (1)
  • formatDurationMilliseconds (36-36)
apps/webapp/app/utils/numberFormatter.ts (2)
  • formatCurrencyAccurate (48-50)
  • formatNumber (15-22)
apps/webapp/app/components/environments/EnvironmentLabel.tsx (1)
  • EnvironmentSlug (141-143)
apps/webapp/app/hooks/useCopy.ts (1)
  • useCopy (3-22)
apps/webapp/app/utils/cn.ts (1)
  • cn (77-79)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (23)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (8, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - npm)
  • GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - pnpm)
  • GitHub Check: typecheck / typecheck
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (19)
apps/webapp/app/components/runs/v3/SharedFilters.tsx (2)

141-206: LGTM!

The labelName parameterization is cleanly threaded through the function, allowing customizable labels while maintaining backward compatibility with the default value.


293-340: LGTM!

The component cleanly supports both controlled mode (via props) and URL-driven mode (via useSearchParams). Only onValueChange is passed down, avoiding the double-calling issue mentioned in past reviews.

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx (7)

1-70: LGTM!

Imports are well-organized and appropriately scoped.


72-104: LGTM!

The access control logic properly handles admin/impersonation bypass and feature flag validation.


112-155: LGTM!

The loader properly validates access, handles missing resources with 404s, and returns the expected data shape.


157-166: LGTM!

The schema correctly uses zod for validation with nullable/optional time filter fields.


255-277: Time filter fallback logic is well-structured.

The handling of different time filter combinations (between, gte, lte, period-based) is comprehensive. The parse-duration library returns null for invalid inputs, so the ?? 7 * 24 * 60 * 60 * 1000 fallback correctly handles malformed period strings.


560-714: LGTM!

The results display logic properly handles all states (loading, error, explain, data) with appropriate conditional rendering and user-friendly error recovery.


804-823: LGTM!

The ScopeItem component cleanly handles all scope types with appropriate labels.

apps/webapp/app/components/code/TSQLResultsTable.tsx (10)

1-61: Import set + sizing constants look solid.
Clear TanStack integration and well-named sizing constants make the later layout logic easy to follow.


70-187: Formatting + fuzzy filter logic is well-aligned.
Combining raw and formatted values is a nice touch for search relevance.


189-215: Debounced input implementation looks good.
State sync + timeout cleanup are handled cleanly.


223-332: Column width heuristics are clear and bounded.
Sampling + min/max constraints should keep sizing stable.


390-403: Right-alignment helper is straightforward.
Nice centralized rule for numeric-like columns.


666-675: EnvironmentSlug swap looks correct.
Keeps display consistent with the updated label component.


677-697: JSON cell truncation/tooltip handling looks good.
Clear and consistent with other truncation patterns.


764-843: Header cell UX (tooltip/sort/filter) looks solid.
Good separation of concerns and readable interactions.


848-884: Filter row wiring is clean.
Focus handling and debounced updates are well-implemented.


886-1183: Virtualized table integration looks solid.
Row model + virtualizer hookup is clean and should scale well.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/webapp/app/components/primitives/DateTime.tsx (1)

104-123: Update callers of formatDateTime that are missing the includeDate parameter.

The includeDate parameter is now required (no default value). At least two locations need updating:

  • apps/webapp/app/v3/services/bulk/BulkActionV2.server.ts lines 339-340: Calls with only 5 arguments (missing includeDate)
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam._index/route.tsx line 574: Call with only 5 arguments (missing includeDate)

These calls will fail at runtime without providing the required 6th parameter.

🤖 Fix all issues with AI agents
In `@apps/webapp/app/components/runs/v3/SharedFilters.tsx`:
- Around line 248-257: The same-day check in the "range" case of
SharedFilters.tsx currently uses from.getDate() === to.getDate(), which falsely
treats different months/years as the same day; replace this with a proper
full-date comparison by importing and using isSameDay from date-fns to compute
the isSameDay boolean, then keep the DateTime rendering logic (DateTime
date={from!} ... and DateTime date={to!} includeDate={!isSameDay} ...) so the
`to` label only hides the date when the two timestamps are truly the same
calendar day.
♻️ Duplicate comments (3)
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/utils.ts (1)

1-19: Guard against NaN/precision loss in stats parsing.

Non‑numeric stat values will propagate NaN into the UI (e.g., "NaN rows read"). Consider finite checks with a safe fallback.

🛠️ Suggested hardening
-  const readRows = parseInt(stats.read_rows, 10);
-  const readBytes = parseInt(stats.read_bytes, 10);
-  const elapsedNs = parseInt(stats.elapsed_ns, 10);
-  const byteSeconds = parseFloat(stats.byte_seconds);
+  const readRows = Number(stats.read_rows);
+  const readBytes = Number(stats.read_bytes);
+  const elapsedNs = Number(stats.elapsed_ns);
+  const byteSeconds = Number(stats.byte_seconds);
+
+  if (![readRows, readBytes, elapsedNs, byteSeconds].every(Number.isFinite)) {
+    return "—";
+  }
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx (2)

387-390: Regex-based triggered_at detection still matches literals/comments.


542-552: Ensure AI time‑filter updates propagate to form state and execution.

After replaceSearchParams, confirm the editor picks up period/from/to and re-submits (or otherwise applies the new bounds), or the AI-suggested time filter won’t take effect until manual submit.

🧹 Nitpick comments (5)
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/utils.ts (1)

22-31: Consider extending units beyond GB for large datasets.

For TB+ values, the formatter caps at GB and produces large GB numbers, which is harder to read. Extending units improves UX.

♻️ Example adjustment
-  const sizes = ["B", "KB", "MB", "GB"];
+  const sizes = ["B", "KB", "MB", "GB", "TB", "PB"];
apps/webapp/app/components/code/AIQueryInput.tsx (1)

33-43: Prefer a type alias instead of an interface for props.

Repository guidelines ask for type in TS/TSX.
As per coding guidelines, please consider the change below.

♻️ Proposed change
-interface AIQueryInputProps {
+type AIQueryInputProps = {
   onQueryGenerated: (query: string) => void;
   /** Called when the AI sets a time filter - updates URL search params */
   onTimeFilterChange?: (filter: AITimeFilter) => void;
   /** Set this to a prompt to auto-populate and immediately submit */
   autoSubmitPrompt?: string;
   /** Change this to force re-submission even if prompt is the same */
   autoSubmitKey?: number;
   /** Get the current query in the editor (used for edit mode) */
   getCurrentQuery?: () => string;
-}
+};
apps/webapp/app/components/runs/v3/SharedFilters.tsx (1)

279-296: Prefer type aliases over interface in TSX here.

Project guidelines call for type aliases in .tsx files.

♻️ Proposed refactor
-export interface TimeFilterApplyValues {
+export type TimeFilterApplyValues = {
   period?: string;
   from?: string;
   to?: string;
-}
+};

-export interface TimeFilterProps {
+export type TimeFilterProps = {
   defaultPeriod?: string;
   period?: string;
   from?: string;
   to?: string;
   /** Label name used in the filter display, defaults to "Created" */
   labelName?: string;
   applyShortcut?: ShortcutDefinition | undefined;
   /** Callback when the user applies a time filter selection, receives the applied values */
   onValueChange?: (values: TimeFilterApplyValues) => void;
-}
+};
As per coding guidelines, prefer `type` aliases here.
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx (1)

358-363: Use a type alias instead of an interface for QueryEditorFormHandle.

♻️ Suggested change
-interface QueryEditorFormHandle {
-  setQuery: (query: string) => void;
-  setScope: (scope: QueryScope) => void;
-  getQuery: () => string;
-  setTimeFilter: (filter: { period?: string; from?: string; to?: string }) => void;
-}
+type QueryEditorFormHandle = {
+  setQuery: (query: string) => void;
+  setScope: (scope: QueryScope) => void;
+  getQuery: () => string;
+  setTimeFilter: (filter: { period?: string; from?: string; to?: string }) => void;
+};

As per coding guidelines, prefer type aliases over interfaces.

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHistoryPopover.tsx (1)

8-46: Prefer matching multi‑word SQL keywords before single‑word ones.

This avoids highlighting only the JOIN portion of LEFT JOIN, RIGHT JOIN, etc.

♻️ Suggested tweak
-  const keywordPattern = new RegExp(
-    `\\b(${SQL_KEYWORDS.map((k) => k.replace(/\s+/g, "\\s+")).join("|")})\\b`,
-    "gi"
-  );
+  const keywordPattern = new RegExp(
+    `\\b(${[...SQL_KEYWORDS]
+      .sort((a, b) => b.length - a.length)
+      .map((k) => k.replace(/\s+/g, "\\s+"))
+      .join("|")})\\b`,
+    "gi"
+  );
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9c1ee99 and f858b1a.

📒 Files selected for processing (10)
  • apps/webapp/app/components/code/AIQueryInput.tsx
  • apps/webapp/app/components/primitives/DateTime.tsx
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • apps/webapp/app/presenters/v3/QueryPresenter.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHistoryPopover.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/utils.ts
  • apps/webapp/app/services/queryService.server.ts
  • internal-packages/database/prisma/migrations/20260120115732_add_time_filter_to_customer_query/migration.sql
  • internal-packages/database/prisma/schema.prisma
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/webapp/app/services/queryService.server.ts
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Use types over interfaces for TypeScript
Avoid using enums; prefer string unions or const objects instead

**/*.{ts,tsx}: Always import tasks from @trigger.dev/sdk, never use @trigger.dev/sdk/v3 or deprecated client.defineJob pattern
Every Trigger.dev task must be exported and have a unique id property with no timeouts in the run function

Files:

  • apps/webapp/app/presenters/v3/QueryPresenter.server.ts
  • apps/webapp/app/components/primitives/DateTime.tsx
  • apps/webapp/app/components/code/AIQueryInput.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHistoryPopover.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/utils.ts
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
{packages/core,apps/webapp}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use zod for validation in packages/core and apps/webapp

Files:

  • apps/webapp/app/presenters/v3/QueryPresenter.server.ts
  • apps/webapp/app/components/primitives/DateTime.tsx
  • apps/webapp/app/components/code/AIQueryInput.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHistoryPopover.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/utils.ts
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use function declarations instead of default exports

Import from @trigger.dev/core using subpaths only, never import from root

Files:

  • apps/webapp/app/presenters/v3/QueryPresenter.server.ts
  • apps/webapp/app/components/primitives/DateTime.tsx
  • apps/webapp/app/components/code/AIQueryInput.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHistoryPopover.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/utils.ts
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
apps/webapp/app/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

Access all environment variables through the env export of env.server.ts instead of directly accessing process.env in the Trigger.dev webapp

Files:

  • apps/webapp/app/presenters/v3/QueryPresenter.server.ts
  • apps/webapp/app/components/primitives/DateTime.tsx
  • apps/webapp/app/components/code/AIQueryInput.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHistoryPopover.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/utils.ts
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
apps/webapp/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

apps/webapp/**/*.{ts,tsx}: When importing from @trigger.dev/core in the webapp, use subpath exports from the package.json instead of importing from the root path
Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webapp

Access environment variables via env export from apps/webapp/app/env.server.ts, never use process.env directly

Files:

  • apps/webapp/app/presenters/v3/QueryPresenter.server.ts
  • apps/webapp/app/components/primitives/DateTime.tsx
  • apps/webapp/app/components/code/AIQueryInput.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHistoryPopover.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/utils.ts
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/otel-metrics.mdc)

**/*.ts: When creating or editing OTEL metrics (counters, histograms, gauges), ensure metric attributes have low cardinality by using only enums, booleans, bounded error codes, or bounded shard IDs
Do not use high-cardinality attributes in OTEL metrics such as UUIDs/IDs (envId, userId, runId, projectId, organizationId), unbounded integers (itemCount, batchSize, retryCount), timestamps (createdAt, startTime), or free-form strings (errorMessage, taskName, queueName)
When exporting OTEL metrics via OTLP to Prometheus, be aware that the exporter automatically adds unit suffixes to metric names (e.g., 'my_duration_ms' becomes 'my_duration_ms_milliseconds', 'my_counter' becomes 'my_counter_total'). Account for these transformations when writing Grafana dashboards or Prometheus queries

Files:

  • apps/webapp/app/presenters/v3/QueryPresenter.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/utils.ts
**/*.{js,ts,jsx,tsx,json,md,yaml,yml}

📄 CodeRabbit inference engine (AGENTS.md)

Format code using Prettier before committing

Files:

  • apps/webapp/app/presenters/v3/QueryPresenter.server.ts
  • apps/webapp/app/components/primitives/DateTime.tsx
  • apps/webapp/app/components/code/AIQueryInput.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHistoryPopover.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/utils.ts
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
internal-packages/database/prisma/migrations/**/*.sql

📄 CodeRabbit inference engine (CLAUDE.md)

internal-packages/database/prisma/migrations/**/*.sql: When editing the Prisma schema, remove extraneous migration lines related to specific tables: _BackgroundWorkerToBackgroundWorkerFile, _BackgroundWorkerToTaskQueue, _TaskRunToTaskRunTag, _WaitpointRunConnections, _completedWaitpoints, SecretStore_key_idx, and unrelated TaskRun indexes
Database indexes must use CONCURRENTLY to avoid table locks and must be in their own separate migration file

Files:

  • internal-packages/database/prisma/migrations/20260120115732_add_time_filter_to_customer_query/migration.sql
🧠 Learnings (6)
📚 Learning: 2026-01-15T11:50:06.044Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.044Z
Learning: Applies to internal-packages/database/prisma/migrations/**/*.sql : When editing the Prisma schema, remove extraneous migration lines related to specific tables: `_BackgroundWorkerToBackgroundWorkerFile`, `_BackgroundWorkerToTaskQueue`, `_TaskRunToTaskRunTag`, `_WaitpointRunConnections`, `_completedWaitpoints`, `SecretStore_key_idx`, and unrelated `TaskRun` indexes

Applied to files:

  • internal-packages/database/prisma/schema.prisma
  • internal-packages/database/prisma/migrations/20260120115732_add_time_filter_to_customer_query/migration.sql
📚 Learning: 2025-12-08T15:19:56.823Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2760
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx:278-281
Timestamp: 2025-12-08T15:19:56.823Z
Learning: In apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx, the tableState search parameter uses intentional double-encoding: the parameter value contains a URL-encoded URLSearchParams string, so decodeURIComponent(value("tableState") ?? "") is required to fully decode it before parsing with new URLSearchParams(). This pattern allows bundling multiple filter/pagination params as a single search parameter.

Applied to files:

  • apps/webapp/app/components/code/AIQueryInput.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/utils.ts
📚 Learning: 2025-11-27T16:26:37.432Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T16:26:37.432Z
Learning: Applies to internal-packages/database/**/*.{ts,tsx} : Use Prisma for database interactions in internal-packages/database with PostgreSQL

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
📚 Learning: 2026-01-08T15:57:09.323Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/otel-metrics.mdc:0-0
Timestamp: 2026-01-08T15:57:09.323Z
Learning: Applies to **/*.ts : Do not use high-cardinality attributes in OTEL metrics such as UUIDs/IDs (envId, userId, runId, projectId, organizationId), unbounded integers (itemCount, batchSize, retryCount), timestamps (createdAt, startTime), or free-form strings (errorMessage, taskName, queueName)

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/utils.ts
📚 Learning: 2026-01-08T15:57:09.323Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/otel-metrics.mdc:0-0
Timestamp: 2026-01-08T15:57:09.323Z
Learning: Applies to **/*.ts : When creating or editing OTEL metrics (counters, histograms, gauges), ensure metric attributes have low cardinality by using only enums, booleans, bounded error codes, or bounded shard IDs

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/utils.ts
📚 Learning: 2026-01-15T11:50:06.044Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.044Z
Learning: Applies to internal-packages/database/prisma/migrations/**/*.sql : Database indexes must use CONCURRENTLY to avoid table locks and must be in their own separate migration file

Applied to files:

  • internal-packages/database/prisma/migrations/20260120115732_add_time_filter_to_customer_query/migration.sql
🧬 Code graph analysis (2)
apps/webapp/app/components/code/AIQueryInput.tsx (1)
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/types.ts (1)
  • AITimeFilter (5-9)
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHistoryPopover.tsx (4)
apps/webapp/app/presenters/v3/QueryPresenter.server.ts (1)
  • QueryHistoryItem (5-15)
apps/webapp/app/components/primitives/Buttons.tsx (1)
  • Button (296-329)
apps/webapp/app/assets/icons/ClockRotateLeftIcon.tsx (1)
  • ClockRotateLeftIcon (1-15)
apps/webapp/app/components/runs/v3/SharedFilters.tsx (1)
  • timeFilterRenderValues (208-277)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (14)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
  • GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
  • GitHub Check: typecheck / typecheck
🔇 Additional comments (23)
internal-packages/database/prisma/schema.prisma (1)

2472-2478: LGTM!

The new time-filter fields (filterPeriod, filterFrom, filterTo) are correctly defined as optional (String?, DateTime?) since time filters are not always applied. The field placement and documentation comments are appropriate.

internal-packages/database/prisma/migrations/20260120115732_add_time_filter_to_customer_query/migration.sql (1)

1-5: LGTM!

The migration is clean and focused, adding only the three new nullable columns. Since this only adds nullable columns without default values, it's a non-blocking DDL operation safe for production tables. No extraneous migration lines present. Based on learnings, the CONCURRENTLY guideline only applies to index creation, which is not applicable here.

apps/webapp/app/presenters/v3/QueryPresenter.server.ts (2)

5-15: LGTM!

The QueryHistoryItem type correctly extends with the new time-filter fields. Using type instead of interface follows the coding guidelines. The nullable types (string | null, Date | null) correctly correspond to the optional Prisma fields (String?, DateTime?).


28-30: LGTM!

The select projection and mapping correctly include the new time-filter fields. Direct pass-through is appropriate since Prisma handles the DateTime to JavaScript Date conversion automatically.

Also applies to: 46-48

apps/webapp/app/components/code/AIQueryInput.tsx (2)

21-29: Time-filter wiring looks solid.

Immediate application on time_filter plus the result fallback makes the flow resilient if an event is missed.

Also applies to: 45-48, 182-207, 219-219


389-392: Status copy updates read well.

apps/webapp/app/components/primitives/DateTime.tsx (4)

1-40: LGTM!

The import cleanup and SSR-compatible timezone handling with useSyncExternalStore are well-implemented. The caching strategy for the local timezone is efficient.


55-102: LGTM - Good SSR hydration fix.

The suppressHydrationWarning wrapper is the correct approach for date/time content that may render differently between server (UTC) and client (local timezone). The new includeDate prop is backward-compatible with its default value of true.


168-336: LGTM - Consistent hydration fix across components.

The suppressHydrationWarning pattern is consistently applied to SmartDateTime, DateTimeAccurateInner, and DateTimeShort components, ensuring SSR compatibility across the entire date/time rendering surface.


399-436: LGTM!

The tooltip content correctly passes includeDate: true explicitly to all formatDateTime calls, ensuring tooltips always display the complete date information regardless of the parent component's includeDate setting.

apps/webapp/app/components/runs/v3/SharedFilters.tsx (4)

141-205: Label-aware time filter rendering looks good.

Nice propagation of labelName into the computed filter display.


298-342: TimeFilter controlled props wiring looks solid.

Nice integration of labelName and onValueChange with existing URL-based defaults.


421-501: Controlled vs URL mode branching reads clean.

The apply path is easy to follow, and the state resets are consistent.


867-870: Good guard on numeric timestamps.

Restricting numeric parsing to fully numeric strings prevents accidental timestamp parsing of ISO values.

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx (8)

1-71: No concerns with the updated imports.


72-78: Helper keeps Date/string normalization straightforward.


263-301: Confirm fallback doesn’t double‑filter when SQL already includes triggered_at.

The UI disables the time filter when the query already filters triggered_at, but the action always passes whereClauseFallback. Please verify executeQuery ignores this fallback in that case, or add a server-side guard to skip it to avoid unexpected extra filtering.


475-491: Good: TimeFilter changes sync state before fetcher submit.


617-707: Results header + table card layout look solid.


721-743: Chart/config split layout is clean and contained.


821-823: Popover trigger swap looks fine.


845-855: Environment label rendering reads well in the scope list.

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHistoryPopover.tsx (1)

79-138: History popover UX and selection flow look good.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@apps/webapp/app/components/primitives/AppliedFilter.tsx`:
- Around line 54-56: In AppliedFilter.tsx, the JSX condition currently uses
"label !== undefined" which still renders the colon when label is null or empty;
change the conditional around the label block (the JSX that renders <div
className="text-text-bright"><span>{label}</span>:) to use a truthy check (e.g.,
"if (label)" or "Boolean(label)") so the whole label block is omitted for
null/empty/other falsy values — update the conditional in the component's
render/return where "label !== undefined" is used.
♻️ Duplicate comments (4)
apps/webapp/app/components/runs/v3/SharedFilters.tsx (2)

247-257: Fix same-day detection to avoid hiding dates across months/years.

Using getDate() alone compares only the day-of-month (1-31), so Jan 5 and Feb 5 would incorrectly be treated as the same day, omitting the date in the to label.

🐛 Proposed fix
     case "range":
       {
-        //If the day is the same, only show the time for the `to` date
-        const isSameDay = from && to && from.getDate() === to.getDate();
+        //If the day is the same, only show the time for the `to` date
+        const sameDay = from && to && isSameDay(from, to);

         valueLabel = (
           <span>
             <DateTime date={from!} includeTime includeSeconds /> –{" "}
-            <DateTime date={to!} includeTime includeSeconds includeDate={!isSameDay} />
+            <DateTime date={to!} includeTime includeSeconds includeDate={!sameDay} />
           </span>
         );
       }

3-17: Missing isSameDay import from date-fns.

The same-day comparison at line 249 uses from.getDate() === to.getDate(), which only compares the day-of-month and will incorrectly treat dates like Jan 5 and Feb 5 as the same day. Import isSameDay from date-fns.

🐛 Proposed fix
 import {
   endOfDay,
   endOfMonth,
   endOfWeek,
+  isSameDay,
   isSaturday,
   isSunday,
   previousSaturday,
   startOfDay,
   startOfMonth,
   startOfWeek,
   startOfYear,
   subDays,
   subMonths,
   subWeeks,
 } from "date-fns";
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx (2)

387-389: Regex may mis-detect triggered_at inside strings/comments.

This is the same limitation noted previously; it can disable the UI even when triggered_at is only in a literal.


543-556: AI time-filter updates only the URL, not the editor state.

handleTimeFilterChange doesn’t use the new setTimeFilter, so AI-selected ranges won’t show up in the TimeFilter UI or affect the next submit unless the user manually changes it.

🐛 Minimal fix (sync UI state)
const handleTimeFilterChange = useCallback(
  (filter: AITimeFilter) => {
    replaceSearchParams({
      period: filter.period,
      from: filter.from,
      to: filter.to,
      cursor: undefined,
      direction: undefined,
    });
+   editorRef.current?.setTimeFilter(filter);
  },
  [replaceSearchParams]
);
🧹 Nitpick comments (2)
apps/webapp/app/components/runs/v3/SharedFilters.tsx (1)

277-295: Prefer type over interface per coding guidelines.

The coding guidelines specify using types instead of interfaces for TypeScript definitions.

♻️ Proposed refactor
-/** Values passed to onApply callback when a time filter is applied */
-export interface TimeFilterApplyValues {
-  period?: string;
-  from?: string;
-  to?: string;
-}
+/** Values passed to onApply callback when a time filter is applied */
+export type TimeFilterApplyValues = {
+  period?: string;
+  from?: string;
+  to?: string;
+};

-export interface TimeFilterProps {
+export type TimeFilterProps = {
   defaultPeriod?: string;
   period?: string;
   from?: string;
   to?: string;
   /** Label name used in the filter display, defaults to "Created" */
   labelName?: string;
   hideLabel?: boolean;
   applyShortcut?: ShortcutDefinition | undefined;
   /** Callback when the user applies a time filter selection, receives the applied values */
   onValueChange?: (values: TimeFilterApplyValues) => void;
-}
+};
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx (1)

357-363: Prefer a type alias over an interface here.

This file is TS-only and the project guideline is to use type aliases.

♻️ Suggested refactor
-interface QueryEditorFormHandle {
-  setQuery: (query: string) => void;
-  setScope: (scope: QueryScope) => void;
-  getQuery: () => string;
-  setTimeFilter: (filter: { period?: string; from?: string; to?: string }) => void;
-}
+type QueryEditorFormHandle = {
+  setQuery: (query: string) => void;
+  setScope: (scope: QueryScope) => void;
+  getQuery: () => string;
+  setTimeFilter: (filter: { period?: string; from?: string; to?: string }) => void;
+};
As per coding guidelines, prefer `type` over `interface` in TS files.
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f858b1a and e1faca8.

📒 Files selected for processing (3)
  • apps/webapp/app/components/primitives/AppliedFilter.tsx
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Use types over interfaces for TypeScript
Avoid using enums; prefer string unions or const objects instead

**/*.{ts,tsx}: Always import tasks from @trigger.dev/sdk, never use @trigger.dev/sdk/v3 or deprecated client.defineJob pattern
Every Trigger.dev task must be exported and have a unique id property with no timeouts in the run function

Files:

  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • apps/webapp/app/components/primitives/AppliedFilter.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
{packages/core,apps/webapp}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use zod for validation in packages/core and apps/webapp

Files:

  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • apps/webapp/app/components/primitives/AppliedFilter.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use function declarations instead of default exports

Import from @trigger.dev/core using subpaths only, never import from root

Files:

  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • apps/webapp/app/components/primitives/AppliedFilter.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
apps/webapp/app/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

Access all environment variables through the env export of env.server.ts instead of directly accessing process.env in the Trigger.dev webapp

Files:

  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • apps/webapp/app/components/primitives/AppliedFilter.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
apps/webapp/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

apps/webapp/**/*.{ts,tsx}: When importing from @trigger.dev/core in the webapp, use subpath exports from the package.json instead of importing from the root path
Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webapp

Access environment variables via env export from apps/webapp/app/env.server.ts, never use process.env directly

Files:

  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • apps/webapp/app/components/primitives/AppliedFilter.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
**/*.{js,ts,jsx,tsx,json,md,yaml,yml}

📄 CodeRabbit inference engine (AGENTS.md)

Format code using Prettier before committing

Files:

  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • apps/webapp/app/components/primitives/AppliedFilter.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
🧠 Learnings (2)
📚 Learning: 2025-12-08T15:19:56.823Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2760
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx:278-281
Timestamp: 2025-12-08T15:19:56.823Z
Learning: In apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx, the tableState search parameter uses intentional double-encoding: the parameter value contains a URL-encoded URLSearchParams string, so decodeURIComponent(value("tableState") ?? "") is required to fully decode it before parsing with new URLSearchParams(). This pattern allows bundling multiple filter/pagination params as a single search parameter.

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
📚 Learning: 2025-11-27T16:26:37.432Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T16:26:37.432Z
Learning: Applies to internal-packages/database/**/*.{ts,tsx} : Use Prisma for database interactions in internal-packages/database with PostgreSQL

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
🧬 Code graph analysis (3)
apps/webapp/app/components/runs/v3/SharedFilters.tsx (3)
apps/webapp/app/components/primitives/DateTime.tsx (1)
  • DateTime (55-102)
apps/webapp/app/hooks/useShortcutKeys.tsx (1)
  • ShortcutDefinition (14-19)
apps/webapp/app/hooks/useSearchParam.ts (1)
  • useSearchParams (7-64)
apps/webapp/app/components/primitives/AppliedFilter.tsx (1)
apps/webapp/app/utils/cn.ts (1)
  • cn (77-79)
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx (14)
apps/webapp/app/components/runs/v3/SharedFilters.tsx (2)
  • timeFilters (139-204)
  • TimeFilter (297-346)
internal-packages/clickhouse/src/index.ts (1)
  • WhereClauseFallback (59-59)
internal-packages/clickhouse/src/client/tsql.ts (1)
  • WhereClauseFallback (28-28)
apps/webapp/app/presenters/v3/QueryPresenter.server.ts (1)
  • QueryHistoryItem (5-15)
apps/webapp/app/v3/querySchemas.ts (1)
  • defaultQuery (430-433)
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHistoryPopover.tsx (1)
  • QueryHistoryPopover (79-139)
apps/webapp/app/components/primitives/Tooltip.tsx (1)
  • SimpleTooltip (141-141)
apps/webapp/app/routes/runs.$runParam.ts (1)
  • loader (12-68)
apps/webapp/app/hooks/useSearchParam.ts (1)
  • useSearchParams (7-64)
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/types.ts (1)
  • AITimeFilter (5-9)
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/utils.ts (1)
  • formatQueryStats (1-20)
apps/webapp/app/components/code/TSQLResultsTable.tsx (1)
  • TSQLResultsTable (886-1184)
apps/webapp/app/components/code/ChartConfigPanel.tsx (1)
  • ChartConfigPanel (80-522)
apps/webapp/app/components/environments/EnvironmentLabel.tsx (1)
  • EnvironmentLabel (78-139)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
  • GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
🔇 Additional comments (15)
apps/webapp/app/components/primitives/AppliedFilter.tsx (2)

22-30: LGTM on the type definition.

Making label optional is a clean approach to support the use case of showing filters without a label.


51-51: Good use of conditional spacing.

The dynamic gap adjustment based on label presence maintains proper visual alignment.

apps/webapp/app/components/runs/v3/SharedFilters.tsx (5)

139-204: LGTM!

The labelName parameter is properly integrated with a sensible default, and all branches correctly propagate it to timeFilterRenderValues.


297-346: LGTM!

The controlled mode integration is clean - prop values correctly override URL params, and only onValueChange is passed to TimeDropdown, avoiding the previously flagged double-apply issue.


421-501: LGTM!

The controlled mode implementation is well-structured:

  • onValueChange skips URL navigation for controlled consumers
  • onApply serves as a post-apply callback for both modes (useful for analytics/side effects)
  • Dependency array is complete

830-844: LGTM!

The shortcut customization with a sensible default (mod+Enter) is a good UX pattern.


864-873: LGTM!

The refined logic correctly distinguishes between numeric timestamps and date strings. The regex approach prevents ISO date strings from being incorrectly parsed as timestamps.

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx (8)

1-70: No review needed for the import adjustments.


72-78: Helper for Date/string normalization looks good.


165-174: Action schema now accepts time-filter inputs—nice.


258-312: Time-filter fallback + history wiring looks solid.


429-493: Time-filter hidden inputs + auto-submit are well wired.


583-749: Results/graph layout refactor looks good.


822-824: Popover trigger change is fine.


846-856: Environment label usage is clear and consistent.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/webapp/app/components/code/QueryResultsChart.tsx (1)

538-551: Avoid padding when filling time gaps.

timeDomain includes padding for axis rendering; using it for fillTimeGaps adds artificial zero buckets at the edges, which can misrepresent the series (e.g., lines dropping to zero outside the real data range). Use the raw min/max timestamps for gap filling and keep padding only for the axis domain.

🛠️ Suggested fix
-    if (isDateBased && timeDomain) {
-      const timestamps = dateValues.map((d) => d.getTime());
-      const dataInterval = detectDataInterval(timestamps);
-      data = fillTimeGaps(
-        data,
-        xDataKey,
-        yAxisColumns,
-        timeDomain[0],
-        timeDomain[1],
-        dataInterval,
-        granularity,
-        aggregation
-      );
-    }
+    if (isDateBased && timeDomain) {
+      const timestamps = dateValues.map((d) => d.getTime());
+      const dataInterval = detectDataInterval(timestamps);
+      const minTime = Math.min(...timestamps);
+      const maxTime = Math.max(...timestamps);
+      data = fillTimeGaps(
+        data,
+        xDataKey,
+        yAxisColumns,
+        minTime,
+        maxTime,
+        dataInterval,
+        granularity,
+        aggregation
+      );
+    }
@@
-  if (isDateBased && timeDomain) {
-    const timestamps = dateValues.map((d) => d.getTime());
-    const dataInterval = detectDataInterval(timestamps);
-    data = fillTimeGaps(
-      data,
-      xDataKey,
-      series,
-      timeDomain[0],
-      timeDomain[1],
-      dataInterval,
-      granularity,
-      aggregation
-    );
-  }
+  if (isDateBased && timeDomain) {
+    const timestamps = dateValues.map((d) => d.getTime());
+    const dataInterval = detectDataInterval(timestamps);
+    const minTime = Math.min(...timestamps);
+    const maxTime = Math.max(...timestamps);
+    data = fillTimeGaps(
+      data,
+      xDataKey,
+      series,
+      minTime,
+      maxTime,
+      dataInterval,
+      granularity,
+      aggregation
+    );
+  }

Also applies to: 607-620

🤖 Fix all issues with AI agents
In `@apps/webapp/app/components/primitives/charts/ChartBar.tsx`:
- Around line 187-195: The conditional rendering for the ReferenceArea is using
truthy checks (enableZoom && zoom?.refAreaLeft && zoom?.refAreaRight) which
prevents zero-valued bounds from rendering; change the condition in ChartBar.tsx
to use nullish checks so 0 is allowed (e.g., check enableZoom and that
zoom.refAreaLeft and zoom.refAreaRight are not null/undefined via != null or !==
null/undefined). Apply the same fix to the other similar block that renders
inspection lines/zoom (the second ReferenceArea rendering block later in the
file using zoom?.refAreaLeft/Right).
♻️ Duplicate comments (2)
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx (2)

389-391: Regex can still false-positive on string literals/comments.

The raw query regex will match triggered_at inside quoted strings or comments. Consider stripping SQL literals/comments before testing.


545-557: AI time-filter updates don’t reach the form state/submission.

handleTimeFilterChange only updates URL search params; the form’s hidden inputs stay stale, so the query results don’t reflect the AI-selected range until a manual change/submit. Please sync the editor state and re-submit.

✅ Suggested fix
 const handleTimeFilterChange = useCallback(
   (filter: AITimeFilter) => {
+    editorRef.current?.setTimeFilter({
+      period: filter.period,
+      from: filter.from,
+      to: filter.to,
+    });
+    editorRef.current?.submit?.();
     replaceSearchParams({
       period: filter.period,
       from: filter.from,
       to: filter.to,
       // Clear cursor/direction when time filter changes
       cursor: undefined,
       direction: undefined,
     });
   },
   [replaceSearchParams]
 );

Also add a submit method on the handle to let the parent trigger a submit after state updates:

-type QueryEditorFormHandle = {
+type QueryEditorFormHandle = {
   setQuery: (query: string) => void;
   setScope: (scope: QueryScope) => void;
   getQuery: () => string;
   setTimeFilter: (filter: { period?: string; from?: string; to?: string }) => void;
+  submit: () => void;
 };
 useImperativeHandle(
   ref,
   () => ({
     setQuery,
     setScope,
     getQuery: () => query,
     setTimeFilter: (filter: { period?: string; from?: string; to?: string }) => {
-      setPeriod(filter.period);
-      setFrom(filter.from);
-      setTo(filter.to);
+      flushSync(() => {
+        setPeriod(filter.period);
+        setFrom(filter.from);
+        setTo(filter.to);
+      });
     },
+    submit: () => {
+      if (formRef.current) {
+        fetcher.submit(formRef.current);
+      }
+    },
   }),
   [query]
 );
🧹 Nitpick comments (2)
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx (1)

360-365: Prefer a type alias for QueryEditorFormHandle.

This file is TSX and the guideline requires type over interface. Please convert to a type alias. As per coding guidelines.

♻️ Proposed change
-interface QueryEditorFormHandle {
-  setQuery: (query: string) => void;
-  setScope: (scope: QueryScope) => void;
-  getQuery: () => string;
-  setTimeFilter: (filter: { period?: string; from?: string; to?: string }) => void;
-}
+type QueryEditorFormHandle = {
+  setQuery: (query: string) => void;
+  setScope: (scope: QueryScope) => void;
+  getQuery: () => string;
+  setTimeFilter: (filter: { period?: string; from?: string; to?: string }) => void;
+};
apps/webapp/app/components/code/QueryResultsChart.tsx (1)

26-33: Prefer a type alias for props.

Use a type alias instead of an interface for QueryResultsChartProps to match the codebase TS guideline.

♻️ Suggested refactor
-interface QueryResultsChartProps {
+type QueryResultsChartProps = {
   rows: Record<string, unknown>[];
   columns: OutputColumnMetadata[];
   config: ChartConfiguration;
   fullLegend?: boolean;
   /** Callback when "View all" legend button is clicked */
   onViewAllLegendItems?: () => void;
-}
+};
As per coding guidelines, use types over interfaces.
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e1faca8 and 88a2895.

📒 Files selected for processing (8)
  • apps/webapp/app/components/code/QueryResultsChart.tsx
  • apps/webapp/app/components/primitives/Dialog.tsx
  • apps/webapp/app/components/primitives/charts/ChartBar.tsx
  • apps/webapp/app/components/primitives/charts/ChartContext.tsx
  • apps/webapp/app/components/primitives/charts/ChartLegendCompound.tsx
  • apps/webapp/app/components/primitives/charts/ChartLine.tsx
  • apps/webapp/app/components/primitives/charts/ChartRoot.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/webapp/app/components/primitives/charts/ChartRoot.tsx
  • apps/webapp/app/components/primitives/charts/ChartContext.tsx
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Use types over interfaces for TypeScript
Avoid using enums; prefer string unions or const objects instead

**/*.{ts,tsx}: Always import tasks from @trigger.dev/sdk, never use @trigger.dev/sdk/v3 or deprecated client.defineJob pattern
Every Trigger.dev task must be exported and have a unique id property with no timeouts in the run function

Files:

  • apps/webapp/app/components/primitives/charts/ChartLegendCompound.tsx
  • apps/webapp/app/components/code/QueryResultsChart.tsx
  • apps/webapp/app/components/primitives/charts/ChartBar.tsx
  • apps/webapp/app/components/primitives/charts/ChartLine.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
  • apps/webapp/app/components/primitives/Dialog.tsx
{packages/core,apps/webapp}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use zod for validation in packages/core and apps/webapp

Files:

  • apps/webapp/app/components/primitives/charts/ChartLegendCompound.tsx
  • apps/webapp/app/components/code/QueryResultsChart.tsx
  • apps/webapp/app/components/primitives/charts/ChartBar.tsx
  • apps/webapp/app/components/primitives/charts/ChartLine.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
  • apps/webapp/app/components/primitives/Dialog.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use function declarations instead of default exports

Import from @trigger.dev/core using subpaths only, never import from root

Files:

  • apps/webapp/app/components/primitives/charts/ChartLegendCompound.tsx
  • apps/webapp/app/components/code/QueryResultsChart.tsx
  • apps/webapp/app/components/primitives/charts/ChartBar.tsx
  • apps/webapp/app/components/primitives/charts/ChartLine.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
  • apps/webapp/app/components/primitives/Dialog.tsx
apps/webapp/app/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

Access all environment variables through the env export of env.server.ts instead of directly accessing process.env in the Trigger.dev webapp

Files:

  • apps/webapp/app/components/primitives/charts/ChartLegendCompound.tsx
  • apps/webapp/app/components/code/QueryResultsChart.tsx
  • apps/webapp/app/components/primitives/charts/ChartBar.tsx
  • apps/webapp/app/components/primitives/charts/ChartLine.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
  • apps/webapp/app/components/primitives/Dialog.tsx
apps/webapp/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

apps/webapp/**/*.{ts,tsx}: When importing from @trigger.dev/core in the webapp, use subpath exports from the package.json instead of importing from the root path
Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webapp

Access environment variables via env export from apps/webapp/app/env.server.ts, never use process.env directly

Files:

  • apps/webapp/app/components/primitives/charts/ChartLegendCompound.tsx
  • apps/webapp/app/components/code/QueryResultsChart.tsx
  • apps/webapp/app/components/primitives/charts/ChartBar.tsx
  • apps/webapp/app/components/primitives/charts/ChartLine.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
  • apps/webapp/app/components/primitives/Dialog.tsx
**/*.{js,ts,jsx,tsx,json,md,yaml,yml}

📄 CodeRabbit inference engine (AGENTS.md)

Format code using Prettier before committing

Files:

  • apps/webapp/app/components/primitives/charts/ChartLegendCompound.tsx
  • apps/webapp/app/components/code/QueryResultsChart.tsx
  • apps/webapp/app/components/primitives/charts/ChartBar.tsx
  • apps/webapp/app/components/primitives/charts/ChartLine.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
  • apps/webapp/app/components/primitives/Dialog.tsx
🧠 Learnings (2)
📚 Learning: 2025-12-08T15:19:56.823Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2760
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx:278-281
Timestamp: 2025-12-08T15:19:56.823Z
Learning: In apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx, the tableState search parameter uses intentional double-encoding: the parameter value contains a URL-encoded URLSearchParams string, so decodeURIComponent(value("tableState") ?? "") is required to fully decode it before parsing with new URLSearchParams(). This pattern allows bundling multiple filter/pagination params as a single search parameter.

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
📚 Learning: 2025-11-27T16:26:37.432Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T16:26:37.432Z
Learning: Applies to internal-packages/database/**/*.{ts,tsx} : Use Prisma for database interactions in internal-packages/database with PostgreSQL

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
🧬 Code graph analysis (5)
apps/webapp/app/components/primitives/charts/ChartLegendCompound.tsx (6)
apps/webapp/app/components/primitives/charts/ChartCompound.tsx (4)
  • ChartLegendCompoundProps (77-77)
  • ChartLegendCompound (101-101)
  • useChartContext (104-104)
  • useSeriesTotal (105-105)
apps/webapp/app/components/primitives/charts/ChartContext.tsx (1)
  • useChartContext (43-49)
apps/webapp/app/components/primitives/charts/ChartRoot.tsx (1)
  • useSeriesTotal (177-190)
apps/webapp/app/utils/cn.ts (1)
  • cn (77-79)
apps/webapp/app/components/primitives/AnimatedNumber.tsx (1)
  • AnimatedNumber (4-16)
apps/webapp/app/components/primitives/Paragraph.tsx (1)
  • Paragraph (88-107)
apps/webapp/app/components/code/QueryResultsChart.tsx (1)
apps/webapp/app/components/primitives/charts/ChartCompound.tsx (1)
  • Chart (92-98)
apps/webapp/app/components/primitives/charts/ChartBar.tsx (6)
apps/webapp/app/components/primitives/charts/ChartCompound.tsx (6)
  • ChartBarRendererProps (75-75)
  • ChartBarRenderer (101-101)
  • useChartContext (104-104)
  • useHasNoData (105-105)
  • useZoomHandlers (106-106)
  • ZoomTooltip (106-106)
apps/webapp/app/components/primitives/charts/ChartContext.tsx (1)
  • useChartContext (43-49)
apps/webapp/app/components/primitives/charts/ChartRoot.tsx (1)
  • useHasNoData (158-171)
apps/webapp/app/components/primitives/charts/ChartZoom.tsx (2)
  • useZoomHandlers (173-244)
  • ZoomTooltip (105-167)
apps/webapp/app/components/primitives/charts/Chart.tsx (2)
  • ChartTooltip (450-450)
  • ChartTooltipContent (451-451)
apps/webapp/app/components/primitives/charts/hooks/useHighlightState.ts (1)
  • getBarOpacity (95-115)
apps/webapp/app/components/primitives/charts/ChartLine.tsx (5)
apps/webapp/app/components/primitives/charts/ChartCompound.tsx (4)
  • ChartLineRendererProps (76-76)
  • ChartLineRenderer (101-101)
  • useChartContext (104-104)
  • useHasNoData (105-105)
apps/webapp/app/components/primitives/charts/ChartContext.tsx (1)
  • useChartContext (43-49)
apps/webapp/app/components/primitives/charts/ChartRoot.tsx (1)
  • useHasNoData (158-171)
apps/webapp/app/components/primitives/charts/ChartLoading.tsx (3)
  • ChartLineLoading (63-73)
  • ChartLineNoData (75-94)
  • ChartLineInvalid (96-115)
apps/webapp/app/components/primitives/charts/Chart.tsx (2)
  • ChartTooltip (450-450)
  • ChartTooltipContent (451-451)
apps/webapp/app/components/primitives/Dialog.tsx (1)
apps/webapp/app/utils/cn.ts (1)
  • cn (77-79)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (21)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (8, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
  • GitHub Check: typecheck / typecheck
  • GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (4)
apps/webapp/app/components/primitives/Dialog.tsx (4)

13-19: DialogPortal wrapper looks good.
Nice encapsulation of the portal container styling.


22-35: DialogOverlay export + styling is solid.
Clear, reusable overlay with consistent animation utilities.


37-52: Fullscreen prop wiring is correct.
Conditional class split is clean and keeps the non-fullscreen behavior intact.


116-125: Export updates are consistent.
Public API now exposes the new primitives as expected.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@apps/webapp/app/components/code/QueryResultsChart.tsx`:
- Around line 815-844: The y-axis domain currently hardcodes [0, "auto"] which
clips negative values; update the logic that builds yAxisProps in
QueryResultsChart.tsx to compute a dynamic lower bound (e.g., min(0,
actualMinValue) or "auto" when no data) instead of always 0, using the available
series/data/aggregations used to render the chart (refer to yAxisProps), so
negative sums/avgs/mins are shown while keeping the axis anchored at zero when
all values are non‑negative.

In
`@apps/webapp/app/routes/_app.orgs`.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx:
- Around line 265-287: timeFilters currently favors period and can ignore
explicit from/to when both are supplied in the URL; to ensure explicit ranges
always win, derive the period argument passed into timeFilters as undefined
whenever from or to are present (e.g., compute const effectivePeriod = (from ||
to) ? undefined : period and call timeFilters({ period: effectivePeriod, from,
to, defaultPeriod: DEFAULT_PERIOD })); then continue using the returned
timeFilter and existing triggeredAtFallback logic (refs: timeFilters,
timeFilter, triggeredAtFallback, DEFAULT_PERIOD, parse).
♻️ Duplicate comments (3)
apps/webapp/app/components/primitives/charts/ChartRoot.tsx (1)

193-206: Totals should be scoped to series keys only.

The current implementation sums every numeric property except dataKey, which could include non-series metadata fields (e.g., timestamps, IDs). This would inflate totals incorrectly.

🐛 Proposed fix
 export function useSeriesTotal(): Record<string, number> {
-  const { data, dataKey } = useChartContext();
+  const { data, dataKeys } = useChartContext();
 
   return useMemo(() => {
-    return data.reduce((acc, item) => {
-      Object.entries(item).forEach(([key, value]) => {
-        if (key !== dataKey) {
-          acc[key] = (acc[key] || 0) + (Number(value) || 0);
-        }
-      });
+    return data.reduce((acc, item) => {
+      dataKeys.forEach((key) => {
+        const value = item[key];
+        acc[key] = (acc[key] || 0) + (Number(value) || 0);
+      });
       return acc;
     }, {} as Record<string, number>);
-  }, [data, dataKey]);
+  }, [data, dataKeys]);
 }
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx (2)

389-392: triggered_at detection can still match strings/comments.

The regex can match triggered_at inside literals/comments, disabling the time filter incorrectly. Consider stripping SQL literals/comments before testing.


545-557: AI time-filter updates don’t flow into the editor state/submit.

handleTimeFilterChange only updates URL params; the editor’s period/from/to state and submission remain unchanged. Consider calling editorRef.current?.setTimeFilter(filter) and triggering a submit (e.g., expose a submit method or lift time filter state).

🧹 Nitpick comments (3)
apps/webapp/app/components/primitives/charts/ChartRoot.tsx (1)

9-37: Consider stronger typing for data prop.

Using any[] for data reduces type safety. A generic type parameter or a record type would provide better compile-time checks.

💡 Suggested improvement
-export type ChartRootProps = {
+export type ChartRootProps<T extends Record<string, unknown> = Record<string, unknown>> = {
   config: ChartConfig;
-  data: any[];
+  data: T[];
   dataKey: string;
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx (1)

359-365: Prefer a type alias over an interface for QueryEditorFormHandle.

This file is TS/TSX; repo guidelines prefer type here.
As per coding guidelines, ...

♻️ Suggested update
-interface QueryEditorFormHandle {
+type QueryEditorFormHandle = {
   setQuery: (query: string) => void;
   setScope: (scope: QueryScope) => void;
   getQuery: () => string;
   setTimeFilter: (filter: { period?: string; from?: string; to?: string }) => void;
-}
+};
apps/webapp/app/components/code/QueryResultsChart.tsx (1)

26-33: Prefer a type alias for props.

♻️ Suggested refactor
-interface QueryResultsChartProps {
+type QueryResultsChartProps = {
   rows: Record<string, unknown>[];
   columns: OutputColumnMetadata[];
   config: ChartConfiguration;
   fullLegend?: boolean;
   /** Callback when "View all" legend button is clicked */
   onViewAllLegendItems?: () => void;
-}
+};
As per coding guidelines, prefer `type` over `interface` for TS props.
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 88a2895 and 25f37f1.

📒 Files selected for processing (3)
  • apps/webapp/app/components/code/QueryResultsChart.tsx
  • apps/webapp/app/components/primitives/charts/ChartRoot.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Use types over interfaces for TypeScript
Avoid using enums; prefer string unions or const objects instead

**/*.{ts,tsx}: Always import tasks from @trigger.dev/sdk, never use @trigger.dev/sdk/v3 or deprecated client.defineJob pattern
Every Trigger.dev task must be exported and have a unique id property with no timeouts in the run function

Files:

  • apps/webapp/app/components/primitives/charts/ChartRoot.tsx
  • apps/webapp/app/components/code/QueryResultsChart.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
{packages/core,apps/webapp}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use zod for validation in packages/core and apps/webapp

Files:

  • apps/webapp/app/components/primitives/charts/ChartRoot.tsx
  • apps/webapp/app/components/code/QueryResultsChart.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use function declarations instead of default exports

Import from @trigger.dev/core using subpaths only, never import from root

Files:

  • apps/webapp/app/components/primitives/charts/ChartRoot.tsx
  • apps/webapp/app/components/code/QueryResultsChart.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
apps/webapp/app/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

Access all environment variables through the env export of env.server.ts instead of directly accessing process.env in the Trigger.dev webapp

Files:

  • apps/webapp/app/components/primitives/charts/ChartRoot.tsx
  • apps/webapp/app/components/code/QueryResultsChart.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
apps/webapp/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

apps/webapp/**/*.{ts,tsx}: When importing from @trigger.dev/core in the webapp, use subpath exports from the package.json instead of importing from the root path
Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webapp

Access environment variables via env export from apps/webapp/app/env.server.ts, never use process.env directly

Files:

  • apps/webapp/app/components/primitives/charts/ChartRoot.tsx
  • apps/webapp/app/components/code/QueryResultsChart.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
**/*.{js,ts,jsx,tsx,json,md,yaml,yml}

📄 CodeRabbit inference engine (AGENTS.md)

Format code using Prettier before committing

Files:

  • apps/webapp/app/components/primitives/charts/ChartRoot.tsx
  • apps/webapp/app/components/code/QueryResultsChart.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
🧠 Learnings (2)
📚 Learning: 2025-12-08T15:19:56.823Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2760
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx:278-281
Timestamp: 2025-12-08T15:19:56.823Z
Learning: In apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx, the tableState search parameter uses intentional double-encoding: the parameter value contains a URL-encoded URLSearchParams string, so decodeURIComponent(value("tableState") ?? "") is required to fully decode it before parsing with new URLSearchParams(). This pattern allows bundling multiple filter/pagination params as a single search parameter.

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
📚 Learning: 2025-11-27T16:26:37.432Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T16:26:37.432Z
Learning: Applies to internal-packages/database/**/*.{ts,tsx} : Use Prisma for database interactions in internal-packages/database with PostgreSQL

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
🧬 Code graph analysis (3)
apps/webapp/app/components/primitives/charts/ChartRoot.tsx (5)
apps/webapp/app/components/primitives/charts/ChartContext.tsx (3)
  • LabelFormatter (11-11)
  • ChartProvider (69-107)
  • useChartContext (43-49)
apps/webapp/app/components/primitives/charts/hooks/useZoomSelection.ts (1)
  • ZoomRange (3-6)
apps/webapp/app/utils/cn.ts (1)
  • cn (77-79)
apps/webapp/app/components/primitives/charts/Chart.tsx (1)
  • ChartContainer (449-449)
apps/webapp/app/components/primitives/charts/ChartLegendCompound.tsx (1)
  • ChartLegendCompound (34-204)
apps/webapp/app/components/code/QueryResultsChart.tsx (1)
apps/webapp/app/components/primitives/charts/ChartCompound.tsx (1)
  • Chart (92-98)
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx (6)
apps/webapp/app/components/runs/v3/SharedFilters.tsx (2)
  • timeFilters (139-204)
  • TimeFilter (297-346)
apps/webapp/app/presenters/v3/QueryPresenter.server.ts (1)
  • QueryHistoryItem (5-15)
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHistoryPopover.tsx (1)
  • QueryHistoryPopover (79-139)
apps/webapp/app/components/primitives/Tooltip.tsx (1)
  • SimpleTooltip (141-141)
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/types.ts (1)
  • AITimeFilter (5-9)
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/utils.ts (1)
  • formatQueryStats (1-20)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (22)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
  • GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (8, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - pnpm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
  • GitHub Check: typecheck / typecheck
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (7)
apps/webapp/app/components/primitives/charts/ChartRoot.tsx (4)

1-7: LGTM!

Imports are well-organized and appropriately scoped.


60-103: LGTM!

Clean compound component composition. Props are correctly distributed between ChartProvider and ChartRootInner, and the JSDoc examples clearly illustrate usage patterns.


116-167: LGTM!

Layout logic correctly handles both fill-container and fixed-height scenarios. The conditional zoom styling and legend placement outside the chart container are well-implemented.


174-187: LGTM!

The hook correctly identifies empty data states while treating zero values as valid data. The logic properly iterates over dataKeys to check all series.

apps/webapp/app/components/code/QueryResultsChart.tsx (3)

3-4: ChartCompound import wiring looks good.


780-792: Legend label formatting for date-based axes looks solid.


699-705: Prop plumbing into Chart.Root and conditional legend behavior look consistent.

Also applies to: 846-893

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@apps/webapp/app/components/code/QueryResultsChart.tsx`:
- Around line 841-852: Move the useMemo hook that computes yAxisDomain so it
runs unconditionally before any early returns: relocate the yAxisDomain =
useMemo(...) block to just after legendLabelFormatter and before the
validation/early return checks in the component so all hooks (including useMemo
for yAxisDomain) are called at the top level and in the same order on every
render.

In `@apps/webapp/app/routes/storybook.charts/route.tsx`:
- Around line 49-57: The handleZoomChange function currently only logs the
ZoomRange and leaves dateRange?.setDateRange commented out, so zooms don't
propagate; update handleZoomChange to call dateRange?.setDateRange(range.start,
range.end) (converting range.start/end to the expected types if necessary) so
the shared DateRangeContext is updated and other components (header, charts)
sync; reference the handleZoomChange function and dateRange?.setDateRange to
locate where to wire this.
♻️ Duplicate comments (1)
apps/webapp/app/components/primitives/charts/ChartBar.tsx (1)

258-269: Allow zero-valued inspection line to render.

The truthy check zoom?.inspectionLine && will skip rendering when the value is 0, which can happen with numeric x-axes. Use a nullish check instead.

Suggested fix
-      {enableZoom && zoom?.inspectionLine && (
+      {enableZoom && zoom?.inspectionLine != null && (
🧹 Nitpick comments (9)
apps/webapp/app/components/primitives/charts/ChartBar.tsx (2)

14-21: Remove unused imports.

cn, ChartConfig, and ChartState are imported but not used in this file.

Suggested fix
 import {
   ChartTooltip,
   ChartTooltipContent,
-  type ChartConfig,
-  type ChartState,
 } from "~/components/primitives/charts/Chart";
-import { cn } from "~/utils/cn";
 import { ChartBarLoading, ChartBarInvalid, ChartBarNoData } from "./ChartLoading";

187-195: Nullish check could be improved for robustness.

The condition uses !== null which doesn't guard against undefined. If zoom exists but refAreaLeft is undefined, the condition undefined !== null evaluates to true, potentially passing invalid values to ReferenceArea.

Suggested fix
-      {enableZoom && zoom?.refAreaLeft !== null && zoom?.refAreaRight !== null && (
+      {enableZoom && zoom?.refAreaLeft != null && zoom?.refAreaRight != null && (
apps/webapp/app/components/code/TSQLResultsTable.tsx (2)

159-166: Consider making undefined values searchable.

Undefined values render as "UNDEFINED" in the UI but the fuzzy filter treats them as empty strings, so searching for "undefined" won't find these cells. For consistency with null handling (which correctly uses "NULL" for both display and search):

Suggested fix
   const rawValue =
     cellValue === null
       ? "NULL"
       : cellValue === undefined
-        ? ""
+        ? "UNDEFINED"
         : typeof cellValue === "object"
           ? JSON.stringify(cellValue)
           : String(cellValue);

214-218: Prefer type over interface per coding guidelines.

Suggested fix
-// Extended column meta to store OutputColumnMetadata
-interface ColumnMeta {
-  outputColumn: OutputColumnMetadata;
-  alignment: "left" | "right";
-}
+// Extended column meta to store OutputColumnMetadata
+type ColumnMeta = {
+  outputColumn: OutputColumnMetadata;
+  alignment: "left" | "right";
+};

Based on coding guidelines: "Use types over interfaces for TypeScript".

apps/webapp/app/components/runs/v3/SharedFilters.tsx (2)

277-295: Use type aliases instead of interfaces for TimeFilter types.

Prefer type aliases here to align with the TS style guidelines.

♻️ Proposed refactor
-export interface TimeFilterApplyValues {
-  period?: string;
-  from?: string;
-  to?: string;
-}
+export type TimeFilterApplyValues = {
+  period?: string;
+  from?: string;
+  to?: string;
+};
 
-export interface TimeFilterProps {
+export type TimeFilterProps = {
   defaultPeriod?: string;
   period?: string;
   from?: string;
   to?: string;
   /** Label name used in the filter display, defaults to "Created" */
   labelName?: string;
   hideLabel?: boolean;
   applyShortcut?: ShortcutDefinition | undefined;
   /** Callback when the user applies a time filter selection, receives the applied values */
   onValueChange?: (values: TimeFilterApplyValues) => void;
-}
+};

As per coding guidelines, prefer types over interfaces in TS.


864-870: Prefer explicit radix in parseInt for numeric timestamps.

This avoids any ambiguity and makes intent explicit.

🧹 Suggested tweak
-    return new Date(parseInt(value));
+    return new Date(parseInt(value, 10));
apps/webapp/app/v3/services/aiQueryService.server.ts (1)

61-100: Consider guarding against empty time-filter inputs.

As-is, the tool accepts {} and will emit { period: undefined, from: undefined, to: undefined }. Consider rejecting empty input or normalizing to a sensible default.

♻️ Possible guard
       execute: async ({ period, from, to }) => {
+        if (!period && !from && !to) {
+          return { success: false, message: "Provide period or from/to for time filter." };
+        }
         // Store the time filter so we can include it in the result
         this.pendingTimeFilter = { period, from, to };
         return {
apps/webapp/app/components/code/QueryResultsChart.tsx (1)

26-33: Prefer a type alias for QueryResultsChartProps.

This repo prefers type over interface, and this block was just touched.

♻️ Proposed refactor
-interface QueryResultsChartProps {
+type QueryResultsChartProps = {
   rows: Record<string, unknown>[];
   columns: OutputColumnMetadata[];
   config: ChartConfiguration;
   fullLegend?: boolean;
   /** Callback when "View all" legend button is clicked */
   onViewAllLegendItems?: () => void;
-}
+};

As per coding guidelines, prefer type aliases.

apps/webapp/app/routes/storybook.charts/route.tsx (1)

28-42: Normalize date inputs before slicing.

The filter assumes startDate/endDate exactly match the dataKey strings. If the range values are Date objects or include time, filtering becomes a no‑op and zoom/range syncing won’t affect the data. Consider normalizing to YYYY-MM-DD (or comparing by Date) before lookup.

♻️ Suggested normalization
-function filterDataByDateRange<T extends Record<string, any>>(
-  data: T[],
-  dataKey: string,
-  startDate: string | undefined,
-  endDate: string | undefined
-): T[] {
-  if (!startDate || !endDate) return data;
+function filterDataByDateRange<T extends Record<string, any>>(
+  data: T[],
+  dataKey: string,
+  startDate: string | Date | undefined,
+  endDate: string | Date | undefined
+): T[] {
+  const normalize = (value?: string | Date) =>
+    value instanceof Date ? value.toISOString().slice(0, 10) : value;
+
+  const start = normalize(startDate);
+  const end = normalize(endDate);
+  if (!start || !end) return data;
 
-  const startIndex = data.findIndex((item) => item[dataKey] === startDate);
-  const endIndex = data.findIndex((item) => item[dataKey] === endDate);
+  const startIndex = data.findIndex((item) => item[dataKey] === start);
+  const endIndex = data.findIndex((item) => item[dataKey] === end);
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 250ffd3 and 001269c.

📒 Files selected for processing (7)
  • apps/webapp/app/components/code/QueryResultsChart.tsx
  • apps/webapp/app/components/code/TSQLResultsTable.tsx
  • apps/webapp/app/components/primitives/AppliedFilter.tsx
  • apps/webapp/app/components/primitives/charts/ChartBar.tsx
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • apps/webapp/app/routes/storybook.charts/route.tsx
  • apps/webapp/app/v3/services/aiQueryService.server.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/webapp/app/components/primitives/AppliedFilter.tsx
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Use types over interfaces for TypeScript
Avoid using enums; prefer string unions or const objects instead

**/*.{ts,tsx}: Always import tasks from @trigger.dev/sdk, never use @trigger.dev/sdk/v3 or deprecated client.defineJob pattern
Every Trigger.dev task must be exported and have a unique id property with no timeouts in the run function

Files:

  • apps/webapp/app/components/primitives/charts/ChartBar.tsx
  • apps/webapp/app/v3/services/aiQueryService.server.ts
  • apps/webapp/app/routes/storybook.charts/route.tsx
  • apps/webapp/app/components/code/TSQLResultsTable.tsx
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • apps/webapp/app/components/code/QueryResultsChart.tsx
{packages/core,apps/webapp}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use zod for validation in packages/core and apps/webapp

Files:

  • apps/webapp/app/components/primitives/charts/ChartBar.tsx
  • apps/webapp/app/v3/services/aiQueryService.server.ts
  • apps/webapp/app/routes/storybook.charts/route.tsx
  • apps/webapp/app/components/code/TSQLResultsTable.tsx
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • apps/webapp/app/components/code/QueryResultsChart.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use function declarations instead of default exports

Import from @trigger.dev/core using subpaths only, never import from root

Files:

  • apps/webapp/app/components/primitives/charts/ChartBar.tsx
  • apps/webapp/app/v3/services/aiQueryService.server.ts
  • apps/webapp/app/routes/storybook.charts/route.tsx
  • apps/webapp/app/components/code/TSQLResultsTable.tsx
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • apps/webapp/app/components/code/QueryResultsChart.tsx
apps/webapp/app/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

Access all environment variables through the env export of env.server.ts instead of directly accessing process.env in the Trigger.dev webapp

Files:

  • apps/webapp/app/components/primitives/charts/ChartBar.tsx
  • apps/webapp/app/v3/services/aiQueryService.server.ts
  • apps/webapp/app/routes/storybook.charts/route.tsx
  • apps/webapp/app/components/code/TSQLResultsTable.tsx
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • apps/webapp/app/components/code/QueryResultsChart.tsx
apps/webapp/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

apps/webapp/**/*.{ts,tsx}: When importing from @trigger.dev/core in the webapp, use subpath exports from the package.json instead of importing from the root path
Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webapp

Access environment variables via env export from apps/webapp/app/env.server.ts, never use process.env directly

Files:

  • apps/webapp/app/components/primitives/charts/ChartBar.tsx
  • apps/webapp/app/v3/services/aiQueryService.server.ts
  • apps/webapp/app/routes/storybook.charts/route.tsx
  • apps/webapp/app/components/code/TSQLResultsTable.tsx
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • apps/webapp/app/components/code/QueryResultsChart.tsx
**/*.{js,ts,jsx,tsx,json,md,yaml,yml}

📄 CodeRabbit inference engine (AGENTS.md)

Format code using Prettier before committing

Files:

  • apps/webapp/app/components/primitives/charts/ChartBar.tsx
  • apps/webapp/app/v3/services/aiQueryService.server.ts
  • apps/webapp/app/routes/storybook.charts/route.tsx
  • apps/webapp/app/components/code/TSQLResultsTable.tsx
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • apps/webapp/app/components/code/QueryResultsChart.tsx
apps/webapp/app/v3/services/**/*.server.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

Organize services in the webapp following the pattern app/v3/services/*/*.server.ts

Files:

  • apps/webapp/app/v3/services/aiQueryService.server.ts
**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/otel-metrics.mdc)

**/*.ts: When creating or editing OTEL metrics (counters, histograms, gauges), ensure metric attributes have low cardinality by using only enums, booleans, bounded error codes, or bounded shard IDs
Do not use high-cardinality attributes in OTEL metrics such as UUIDs/IDs (envId, userId, runId, projectId, organizationId), unbounded integers (itemCount, batchSize, retryCount), timestamps (createdAt, startTime), or free-form strings (errorMessage, taskName, queueName)
When exporting OTEL metrics via OTLP to Prometheus, be aware that the exporter automatically adds unit suffixes to metric names (e.g., 'my_duration_ms' becomes 'my_duration_ms_milliseconds', 'my_counter' becomes 'my_counter_total'). Account for these transformations when writing Grafana dashboards or Prometheus queries

Files:

  • apps/webapp/app/v3/services/aiQueryService.server.ts
🧠 Learnings (2)
📚 Learning: 2025-06-10T09:31:01.040Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 2158
File: internal-packages/clickhouse/src/client/queryBuilder.ts:77-92
Timestamp: 2025-06-10T09:31:01.040Z
Learning: ClickHouse SQL syntax requires GROUP BY clause to come before ORDER BY clause, following standard SQL clause ordering: SELECT, FROM, WHERE, GROUP BY, HAVING, ORDER BY, LIMIT.

Applied to files:

  • apps/webapp/app/v3/services/aiQueryService.server.ts
📚 Learning: 2026-01-15T11:50:06.067Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.067Z
Learning: Applies to internal-packages/clickhouse/schema/**/*.sql : Follow ClickHouse naming conventions: `raw_` prefix for input tables, `_v1`, `_v2` suffixes for versioning, `_mv_v1` suffix for materialized views

Applied to files:

  • apps/webapp/app/v3/services/aiQueryService.server.ts
🧬 Code graph analysis (3)
apps/webapp/app/v3/services/aiQueryService.server.ts (2)
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/types.ts (1)
  • AITimeFilter (5-9)
internal-packages/tsql/src/query/schema.ts (1)
  • TableSchema (285-303)
apps/webapp/app/routes/storybook.charts/route.tsx (9)
apps/webapp/app/components/primitives/charts/DateRangeContext.tsx (4)
  • formatISODate (55-58)
  • formatISODateLong (63-66)
  • useDateRange (110-116)
  • DateRangeProvider (70-108)
apps/webapp/app/components/primitives/charts/ChartCompound.tsx (4)
  • ChartState (72-72)
  • ZoomRange (73-73)
  • Chart (92-98)
  • ChartConfig (72-72)
apps/webapp/app/components/primitives/charts/Chart.tsx (2)
  • ChartState (9-9)
  • ChartConfig (11-20)
apps/webapp/app/components/primitives/charts/hooks/useZoomSelection.ts (1)
  • ZoomRange (3-6)
apps/webapp/app/components/primitives/RadioButton.tsx (2)
  • RadioGroup (93-98)
  • RadioGroupItem (112-165)
apps/webapp/app/components/primitives/Paragraph.tsx (1)
  • Paragraph (88-107)
apps/webapp/app/components/primitives/charts/Card.tsx (1)
  • Card (5-16)
apps/webapp/app/components/primitives/SegmentedControl.tsx (1)
  • SegmentedControl (80-152)
apps/webapp/app/components/primitives/charts/BigNumber.tsx (1)
  • BigNumber (15-46)
apps/webapp/app/components/code/QueryResultsChart.tsx (1)
apps/webapp/app/components/primitives/charts/ChartCompound.tsx (1)
  • Chart (92-98)
🪛 Biome (2.1.2)
apps/webapp/app/components/code/QueryResultsChart.tsx

[error] 841-842: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.

Hooks should not be called after an early return.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (21)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (8, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
  • GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
  • GitHub Check: typecheck / typecheck
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (30)
apps/webapp/app/components/primitives/charts/ChartBar.tsx (3)

40-55: LGTM with minor type safety note.

The type definitions correctly use type over interface per coding guidelines. The any type on line 50 for payload is acceptable given recharts' loose typing, but could be tightened if a more specific type is available from recharts.


83-99: Event handlers are well-structured.

Good use of useCallback with appropriate dependencies. The handlers correctly coordinate zoom and highlight state.


197-238: Bar rendering logic is well-implemented.

The stacked bar rendering with per-cell opacity based on highlight state is cleanly implemented. Minor nit: the index parameter on line 214 is declared but unused.

apps/webapp/app/components/code/TSQLResultsTable.tsx (9)

49-61: LGTM!

Constants are well-documented and the RowData type alias properly uses a type (not interface) per coding guidelines.


103-121: DateTime formatting now correctly uses toLocaleString.

The previous issue regarding toLocaleDateString ignoring time options has been properly fixed. The code now uses toLocaleString which correctly includes hour, minute, and second components in the output.


189-212: LGTM!

The debounced input implementation is clean and correctly uses forwardRef. The onChange dependency in the effect is fine since TanStack Table's setFilterValue is stable.


299-329: LGTM!

The column width calculation with sampling (first 100 rows) is a good performance optimization. The bounds checking ensures reasonable column sizes.


402-431: Good performance optimization.

The CellValueWrapper pattern that only enables tooltips when hovered is a smart optimization for tables with many cells.


696-756: LGTM!

The copyable cell implementation provides good UX with hover-to-reveal copy button and visual feedback on copy success.


758-840: LGTM!

Header cell component cleanly handles sorting, filtering, tooltips, and alignment with proper accessibility attributes.


949-954: LGTM!

The virtualizer configuration with overscan: 20 provides smooth scrolling without excessive DOM nodes. The estimated row height and scroll element reference are correctly configured.


1134-1177: Virtualization implemented correctly.

The tbody uses absolute positioning with translateY transforms for efficient rendering of visible rows only. The grid-based layout properly handles variable column widths with resize support.

apps/webapp/app/components/runs/v3/SharedFilters.tsx (4)

139-203: Label-aware timeFilters flow looks solid.

The labelName propagation and render-value wiring is consistent and keeps default handling intact.


206-275: Range render values handle same-day display cleanly.

The from/to display logic is clear and keeps the label semantics intact.


297-342: TimeFilter label/controlled wiring looks good.

Prop handling is clean and the applied filter label/value rendering remains consistent.


421-489: Controlled vs URL mode application is well separated.

The value construction and navigation split reads cleanly and should be easy to extend.

apps/webapp/app/v3/services/aiQueryService.server.ts (8)

10-14: Centralized AITimeFilter type export looks good.

Type-only import + re-export keeps compatibility clean.


29-31: Updated AIQueryResult union is clear.

The optional timeFilter on success reads well.


148-148: Nice DRY reuse of the shared setTimeFilter tool.

Centralizing the tool builder keeps stream and non-stream behavior aligned.

Also applies to: 214-214


161-166: Accessor for pending time filter is straightforward.

Simple and clear.


236-236: Including timeFilter in success result is helpful.

Makes downstream usage explicit and avoids extra lookups.


425-441: Prompt guidance for time filtering is clear and consistent.

The new rules should steer the model away from triggered_at filtering.

Also applies to: 518-525


22-24: The time_filter events are actively emitted in the streaming pipeline. When the AI calls the setTimeFilter tool, the route handler (apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query.ai-generate.tsx, lines 129-136) explicitly detects this tool call and immediately emits a time_filter event with the filter arguments. The event is then consumed by the UI component (AIQueryInput.tsx, lines 198-200) which applies the time filter via the onTimeFilterChange callback. The streaming pipeline is working as intended.

Likely an incorrect or invalid review comment.


54-55: Remove comment: AIQueryService is instantiated per-request, not reused across concurrent requests.

The AIQueryService is created as a local variable within the action function of the route handler (line 87 in query.ai-generate.tsx). In Remix, the action function is invoked once per HTTP request, so each incoming request receives its own isolated instance. The pendingTimeFilter state is reset and used entirely within a single request lifecycle, with no possibility of cross-request interference. The concern about concurrent requests overwriting shared state does not apply.

apps/webapp/app/components/code/QueryResultsChart.tsx (4)

3-4: LGTM.


699-705: Legend prop wiring looks consistent.

Also applies to: 861-898


780-792: Legend label formatter addition looks good.


815-839: Axis prop split reads clean.

apps/webapp/app/routes/storybook.charts/route.tsx (2)

267-275: Story wrapper looks solid.

Wrapping the dashboard in DateRangeProvider keeps the demo’s shared range centralized and easy to reset.


683-767: Chart configs are nicely typed.

Using satisfies ChartConfig keeps series keys/labels consistent and avoids silent misconfigurations.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@matt-aitken matt-aitken marked this pull request as ready for review January 20, 2026 18:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants